const pool = require('./browser-pool');
const util = require('./util');
const path = require('path');
const fse = require('fs-extra');
const { S3Client, ListBucketsCommand, PutObjectCommand, CreateBucketCommand } = require("@aws-sdk/client-s3");
const profile = require('../profile')
const REGION = profile.REGION;
const bucketName = profile.bucketName;
const fileS3Path = profile.fileS3Path;

/**
 * 生成pdf
 * @param generateConfig {GenerateConfig}
 * @param option {PdfOption | IMGOption}
 * @param type 1 pdf，2 img
 * @return {Promise<string>}
 */
module.exports = async function (generateConfig, option, type = 1) {
    const {url, cookie, host, referer, waitFor, waitForError, waitUntil, timeout, proxy, viewport} = generateConfig;
    let date = new Date,
        now = date.getTime(),
        prevTime = now,
        startTime = now,
        taskId = `${now.toString(16).toUpperCase()}`,
        logs = [];

    tickLog('当前时间', `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`);
    let page, proxyBrowser;
    if (proxy) {
        tickLog('proxy', proxy);
        proxyBrowser = await pool.proxyBrowser(proxy);
        page = await pool.createPage(proxyBrowser);
    } else {
        page = await pool.newPage();
    }
    try {
        if (viewport) {
            await page.setViewport(viewport);
        }
        // 处理cookie
        await clearCookie(page, url);
        if (cookie && host) {
            await setCookies(page, cookie, host);
        }
        tickLog('cookie 处理完成');

        const newUrl = url + (url.indexOf('?') > -1 ? '&' : '?') + now;
        await page.goto(newUrl, {referer: referer || url, waitUntil, timeout});
        tickLog('页面加载完成');

        if (waitFor) { // 读取准备就绪标记
            const waits = [];
            if (waitFor) {
                waits.push(
                    page.waitForFunction(waitFor).then(finish('waitFor'))
                )
            }
            if (waitForError) {
                waits.push(// 异常通常在5s内触发，若超时，则表示未触发异常
                    page.waitForFunction(waitForError, {
                        timeout: 5000
                    }).then(finish('waitForError'), finish('waitFor'))
                )
            }
            const value = await Promise.race(waits); // 赛跑，监听器和异常谁先触发
            if (value === 'waitForError') {
                throw `页面加载出错`;
            }

            function finish(value) {
                return () => value;
            }

            tickLog(`页面 waitFor =【${waitFor}】 表达式执行成功`);
        }

        let buffer, ext
        if (type === 1) {
            ext = 'pdf'
            buffer = await createPDF(option);
        } else if (type === 2) {
            ext = getImgType((option || {}).type);
            buffer = await createIMG(option);
        } else {
            throw `参数错误，type=${type} 是未知操作`;
        }


        // const filePath = util.generatePath(Date.now(), ext);
        // const outputFilePath = path.resolve(util.generateBasePath, filePath)
        // await fse.outputFile(outputFilePath, buffer);
        // tickLog('输出outputFilePath', outputFilePath);

        // Set the AWS region
        // const REGION = "cn-northwest-1";
        // Set the bucket parameters
        // const bucketName = "iplus-aws-test-bucket";
        // const fileS3Path = 'filesvr/pdf-generate/';
        const date = new Date();

        let fileName = '/' + `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}/${Date.now()}.${ext}`;
        const key = fileS3Path + fileName;

        tickLog('s3路径', key);
        const objectParams = { Bucket: bucketName, Key: key, Body: buffer };
        const s3 = new S3Client({ region: REGION });
        const run = async () => {
            try {
                const results = await s3.send(new PutObjectCommand(objectParams));
                console.log("Successfully uploaded data to " + bucketName + key );
            } catch (err) {
                console.log("Error", err);
            }
        };
        await run();
        return fileName;

    } catch (e) {
        let msg = String(e.message || e);
        if (msg.includes('Navigation timeout') || msg.includes('net::ERR_CONNECTION_REFUSED')) {
            msg = `访问页面失败，请检查网络链接或者页面是否可以正确访问 ERR: ${msg}`;
        } else if (msg.includes('waiting for function failed') && waitFor) {
            msg = `等待页面表达式 waitFor =【"${waitFor}"】超时，请检查表达式是否正确`;
        }

        const buffer = await page.screenshot({fullPage: true}); // 截图
        const filePath = util.generatePath(taskId, 'png');
        const outputFilePath = path.resolve(util.generateBasePath, filePath)
        await fse.outputFile(outputFilePath, buffer);

        msg = msg + '； 异常截图 = ' + filePath;

        tickErrLog('发生异常：', msg);
        if (page.jsError) {
            tickErrLog('jsError', page.jsError.join('\n'));
        }

        throw `【${taskId}】 ${msg}`;
    } finally {
        page.jsError = [];
        if (proxy && proxyBrowser && page) {
            await pool.closePage(page);
            proxyBrowser.disconnect();
            tickLog('proxy', 'disconnect');
        } else {
            if (!page.isClosed()) {
                await page.goto('about:blank');
            }
            pool.release(page); // 释放资源
        }
        flushLog();
    }

    /**
     * pdf生成
     * @param option {PdfOption}
     * @return {Promise<string>}
     */
    async function createPDF(option) {
        if (!option) { // 读取页面pdf配置
            option = await page.evaluate('window.PdfOption') || {};
        }

        const buffer = await page.pdf({
            format: 'A4',
            printBackground: true,
            ...option
        });
        tickLog('转换PDF成功');

        return buffer;
    }

    /**
     * @param option {IMGOption}
     * @return {Promise<string>}
     */
    async function createIMG(option) {
        if (!option) { // 读取页面配置
            option = (await page.evaluate('window.ImgOption')) || {};
        }

        let buffer;
        if (option.type === 'jpg') {
            option.type = 'jpeg';
        }

        if (option.screenshotSelector) {
            // dom 截图
            // @see https://zhaoqize.github.io/puppeteer-api-zh_CN/#?product=Puppeteer&version=v5.3.0&show=api-elementhandlescreenshotoptions
            const dom = await page.$(option.screenshotSelector);
            buffer = await dom.screenshot(option);
            await dom.dispose();
        } else {
            // 页面截图
            // @see https://zhaoqize.github.io/puppeteer-api-zh_CN/#?product=Puppeteer&version=v5.3.0&show=api-pagescreenshotoptions
            buffer = await page.screenshot(option);
        }

        tickLog('转换IMG成功');

        return buffer;
    }

    function tickErrLog(...args) {
        now = Date.now();
        const log = [...args, now - prevTime, 'ms'];
        log.method = 'error';
        logs.push(log);
        prevTime = now;
    }

    function tickLog(...args) {
        now = Date.now();
        const log = [...args, now - prevTime, 'ms'];
        log.method = 'log';
        logs.push(log);
        prevTime = now;
    }

    function flushLog() {
        console.log('');
        console.log('');
        console.log('Start')
        console.log('  ', 'URL', url)
        console.log('  ', 'TaskId', taskId)
        logs.forEach(log => console[log.method || 'log']('  ', ...log));
        console.log('End', '总耗时', Date.now() - startTime, 'ms');
        console.log('');
    }
};

function getImgType(type) {
    if (!type) {
        return 'png'
    }
    if (/^jpe?g$/i.test(type)) {
        return 'jpg';
    }
}

/**
 * 删除
 * @param page {Page}
 * @param host
 * @return {Promise<void>}
 */
async function clearCookie(page, host) {
    try {
        const cookies = await page.cookies(host);
        await page.deleteCookie(...cookies);
    } catch (e) {
    }
}

async function setCookies(page, cookie, domain) {
    const cookies = cookie.split(';').map(v => {
        const [name, value] = v.trim().split('=');
        return {name, value, domain}
    })
    await page.setCookie(...cookies);
}
